using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using System.Drawing.Imaging;
using ImageResizer.Properties;

namespace ImageResizer
{
    /// <summary>
    /// Dialog which resize a list of pictures
    /// </summary>
    public partial class ProcessDialog : Form
    {
        #region Private vars
        /// <summary>
        /// List of files to process
        /// </summary>
        private IList<FileToProcess> _listOfImagesToProcess;

        /// <summary>
        /// List of filename to process (from CTOR parameter)
        /// </summary>
        private IEnumerable<string> _inputListOfImagesToProcess;

        /// <summary>
        /// The configuration file
        /// </summary>
        private AlbulleConfigurationFile _configurationFile;

        /// <summary>
        /// Default nameof the album
        /// </summary>
        private string _defaultAlbumName;

        /// <summary>
        /// Taille des photos
        /// </summary>
        private Size _defaultPhotoSize;

        #endregion

        #region Private nested classes

        /// <summary>
        /// Container for a picture to process (contains all output names)
        /// </summary>
        private class FileToProcess
        {
            /// <summary>
            /// Gets the initial file to resize
            /// </summary>
            public string InitialFile { get; private set; }

            /// <summary>
            /// Gets the picture normal output (=used to display image max 1900 x 1200)
            /// </summary>
            public string OutputPhoto { get; private set; }

            /// <summary>
            /// Gets the original picture (no size limitation,for download oinly)
            /// </summary>
            public string OutputOriginal { get; private set; }

            /// <summary>
            /// Gets the Thumbnail picture name (for galery)
            /// </summary>
            public string OutputThumbGalerie { get; private set; }

            /// <summary>
            /// Gets the Thumbnail picture name (for diaporama)
            /// </summary>
            public string OutputThumbDiaporama { get; private set; }

            /// <summary>
            /// Gets the album name
            /// </summary>
            private string AlbumName { get; set; }


            /// <summary>
            /// Gets the configuration file parameters
            /// </summary>
            public AlbulleConfigurationFile ConfigurationFile { get; private set; }


            /// <summary>
            /// CTOR
            /// </summary>
            /// <param name="filename">a filename</param>
            /// <param name="albumName">the album name</param>
            /// <param name="configurationFile">the Albulle configuration file</param>
            public FileToProcess(string filename, string albumName, AlbulleConfigurationFile configurationFile)
            {
                ConfigurationFile = configurationFile;
                InitialFile = filename;
                AlbumName = albumName + "/";
                if(!Directory.Exists(Path.Combine(configurationFile.PhotoFolder, AlbumName)))
                    Directory.CreateDirectory(Path.Combine(configurationFile.PhotoFolder, AlbumName));

                if(!Directory.Exists(Path.Combine(configurationFile.OriginalFolder, AlbumName)))
                    Directory.CreateDirectory(Path.Combine(configurationFile.OriginalFolder, AlbumName));

                OutputPhoto = Path.Combine(Path.Combine(configurationFile.PhotoFolder, AlbumName), Path.GetFileName(InitialFile));
                OutputOriginal = Path.Combine(Path.Combine(configurationFile.OriginalFolder, AlbumName), Path.GetFileName(InitialFile));


                string thumbPhoto = Path.Combine(Path.Combine(configurationFile.PhotoFolderRelativeInitialPath, AlbumName), Path.GetFileName(InitialFile));
                string thumbGalName = Path.GetFileNameWithoutExtension(InitialFile) + "_" + ConfigurationFile.ThumbnailGalerieSize.Width + "x" + ConfigurationFile.ThumbnailGalerieSize.Height + "_" + MD5(thumbPhoto) + Path.GetExtension(InitialFile);
                string thumbDiaName = Path.GetFileNameWithoutExtension(InitialFile) + "_" + ConfigurationFile.ThumbnailDiaporamaSize.Width + "x" + ConfigurationFile.ThumbnailDiaporamaSize.Height + "_" + MD5(thumbPhoto) + Path.GetExtension(InitialFile);
 
                OutputThumbGalerie = Path.Combine(configurationFile.ThumbnailFolder, thumbGalName);
                OutputThumbDiaporama = Path.Combine(configurationFile.ThumbnailFolder, thumbDiaName);
            }


            #region Private methods
            /// <summary>
            /// Crypt a string in MD5 (like php.md5())
            /// </summary>
            /// <param name="textToCrypt">the text to crypt</param>
            /// <returns>the cipher text</returns>
            private static string MD5(string textToCrypt)
            {
                byte[] textBytes = System.Text.Encoding.Default.GetBytes(textToCrypt);
                System.Security.Cryptography.MD5CryptoServiceProvider cryptHandler = new System.Security.Cryptography.MD5CryptoServiceProvider();
                byte[] hash = cryptHandler.ComputeHash(textBytes);
                string ret = "";
                foreach (byte a in hash)
                {
                    if (a < 16)
                        ret += "0" + a.ToString("x");
                    else
                        ret += a.ToString("x");
                }
                return ret;
            } 
            #endregion

        } 

        #endregion

        #region CTOR

        /// <summary>
        /// CTOR
        /// </summary>
        /// <param name="listOfImagesToProcess">list of filename to resize</param>
        /// <param name="defaultAlbumName">the default album name</param>
        public ProcessDialog(IEnumerable<string> listOfImagesToProcess, string defaultAlbumName)
        {
            InitializeComponent();

            //read config file
            _configurationFile = new AlbulleConfigurationFile(Settings.Default.AlbulleConfig, Settings.Default.AlbulleConfig, "/");

            _inputListOfImagesToProcess = listOfImagesToProcess;
            _defaultAlbumName = defaultAlbumName;

            try
            {
                _defaultPhotoSize = new Size(Settings.Default.PhotoWidth, Settings.Default.PhotoHeight);
            }
            catch
            {
                _defaultPhotoSize = new Size(1600, 1200);
            }

        } 
        #endregion

        #region Background worker methods
        /// <summary>
        /// Main working methods. Process all files
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BackgroundWorkerProcessingImagesDoWork(object sender, DoWorkEventArgs e)
        {
            IList<FileToProcess> listOfImagesToProcess = e.Argument as IList<FileToProcess>;
            int counter = 1;
            if (listOfImagesToProcess != null)
            {
                foreach (FileToProcess file in listOfImagesToProcess)
                {
                    backgroundWorkerProcessingImages.ReportProgress(counter);
                    BackgroundWorkerProcessingImagesProcessOneFile(file);
                    counter++;
                    if (e.Cancel)
                        break;
                }
            }
        }

        /// <summary>
        /// Updates GUI each time a picture is resized
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e">contains progressioninformation</param>
        private void BackgroundWorkerProcessingImagesProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            labelProgession.Text = String.Format("Resizing image {0} / {1}...", e.ProgressPercentage, _listOfImagesToProcess.Count);
            progressBar1.Value = (int)(e.ProgressPercentage * 100.0 / _listOfImagesToProcess.Count);
            pictureBoxCurrent.Image = Image.FromFile(_listOfImagesToProcess[e.ProgressPercentage-1].InitialFile);
        }

        /// <summary>
        /// Manage the end of the resizing process
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BackgroundWorkerProcessingImagesRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            DialogResult = DialogResult.OK;
            Close();
        }
        #endregion

        #region Private methods (resize)
        /// <summary>
        /// Resize an image
        /// </summary>
        /// <param name="file">the parameters</param>
        private void BackgroundWorkerProcessingImagesProcessOneFile(FileToProcess file)
        {
             //resize pictures....
            Image orig = Image.FromFile(file.InitialFile);
            if (ExifImageInfo.NeedRotate(orig))
            {
                if (ExifImageInfo.GetOrientation(orig) == Orientation.Left)
                    orig.RotateFlip(RotateFlipType.Rotate90FlipNone);
                if (ExifImageInfo.GetOrientation(orig) == Orientation.Right)
                    orig.RotateFlip(RotateFlipType.Rotate270FlipNone);
            }

            Image photo = Manina.Windows.Forms.Utility.ThumbnailFromImage(orig, _defaultPhotoSize, Color.Transparent);
            Image thumbGal = Manina.Windows.Forms.Utility.ThumbnailFromImage(orig, file.ConfigurationFile.ThumbnailGalerieSize, Color.Transparent);
            Image thumbDiapo = Manina.Windows.Forms.Utility.ThumbnailFromImage(orig, file.ConfigurationFile.ThumbnailDiaporamaSize, Color.Transparent);

            //Image photo = Manina.Windows.Forms.Utility.ThumbnailFromFile(file.InitialFile, _defaultPhotoSize, Manina.Windows.Forms.UseEmbeddedThumbnails.Auto, Color.Transparent);
            //Image thumbGal = Manina.Windows.Forms.Utility.ThumbnailFromFile(file.InitialFile, file.ConfigurationFile.ThumbnailGalerieSize, Manina.Windows.Forms.UseEmbeddedThumbnails.Auto, Color.Transparent);
            //Image thumbDiapo = Manina.Windows.Forms.Utility.ThumbnailFromFile(file.InitialFile, file.ConfigurationFile.ThumbnailDiaporamaSize, Manina.Windows.Forms.UseEmbeddedThumbnails.Auto, Color.Transparent);
 
            //save pictures
            photo.SaveJpeg(file.OutputPhoto, 80);
            thumbGal.SaveJpeg(file.OutputThumbGalerie, 80);
            thumbDiapo.SaveJpeg(file.OutputThumbDiaporama, 80);
            File.Copy(file.InitialFile, file.OutputOriginal, true);
        }

        #endregion

        #region Private methods (ui)
        /// <summary>
        /// Initialize dialog box
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ProcessDialog_Load(object sender, EventArgs e)
        {
            DirectoryInfo di = new DirectoryInfo(_configurationFile.PhotoFolder);
            //comboBox_Album.Items.AddRange(di.GetDirectories());
            FillComboBox(di, "");
            comboBox_Album.Items.Add(Resources.ComboBoxSeparator);
            comboBox_Album.Items.Add(Resources.ProcessDialog_ProcessDialog_Load_Nouvel_album___);
        }

        private void FillComboBox(DirectoryInfo di, string relativePath)
        {
            foreach (DirectoryInfo d in di.GetDirectories())
            {

                if (!String.IsNullOrEmpty(relativePath))
                {
                    comboBox_Album.Items.Add(relativePath + "/" + d);
                    FillComboBox(d, relativePath + "/" + d);
                }
                else
                {
                    comboBox_Album.Items.Add(d);
                    FillComboBox(d, d.ToString());
                }
            }
        }

        /// <summary>
        /// Starts to resize process
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButtonStartClick(object sender, EventArgs e)
        {
            if (comboBox_Album.SelectedIndex == -1)
                MessageBox.Show(Resources.ProcessDialog_button_start_Click_Choississez_un_album__);

            button_cancel.Enabled = true;
            button_start.Enabled = false;
            comboBox_Album.Enabled = false;


            //create the list of objects to process
            _listOfImagesToProcess = new List<FileToProcess>();
            foreach (string filename in _inputListOfImagesToProcess)
                _listOfImagesToProcess.Add(new FileToProcess(filename, comboBox_Album.SelectedItem.ToString(), _configurationFile));

            //start background worker
            backgroundWorkerProcessingImages.RunWorkerAsync(_listOfImagesToProcess);
        }

        /// <summary>
        /// Cancel the background worker and ends the dialog
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButtonCancelClick(object sender, EventArgs e)
        {
            backgroundWorkerProcessingImages.CancelAsync();
            while (backgroundWorkerProcessingImages.IsBusy)
            {
                System.Threading.Thread.Sleep(500);
            }
            DialogResult = DialogResult.Cancel;
            Close();
        }

        /// <summary>
        /// Manage the new album making
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ComboBox1SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboBox_Album.SelectedIndex == comboBox_Album.Items.Count - 1)
            {
                AskNameDialog askAlbumName = new AskNameDialog(Resources.ProcessDialog_ComboBox1SelectedIndexChanged_Nom_du_nouvel_album_, _defaultAlbumName);
                if (askAlbumName.ShowDialog() == DialogResult.OK)
                {
                    comboBox_Album.Items.Insert(0, askAlbumName.Result);
                    comboBox_Album.SelectedIndex = 0;
                }
                else
                {
                    comboBox_Album.SelectedIndex = -1;
                }
            }
        }
        #endregion    
    } 
    
    #region Static class for Extension methods
    /// <summary>
    /// Static class for imaging functions
    /// </summary>
    internal static class MyImagingExtension
    {
        /// <summary>
        /// Get encoder info
        /// </summary>
        /// <param name="mimeType">the mimetype (string: for example : image/jpeg</param>
        /// <returns>the encoder info (matching mimetype) or null if unknown</returns>
        private static ImageCodecInfo GetEncoderInfo(string mimeType)
        {
            // Get image codecs for all image formats
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();

            // Find the correct image codec
            foreach (ImageCodecInfo t in codecs)
                if (t.MimeType == mimeType)
                    return t;
            return null;
        }


        /// <summary>
        /// Extension methods for class Image.
        /// Save an image into a JPeg
        /// </summary>
        /// <param name="img">the image to save</param>
        /// <param name="outputFileName">the outputfilename</param>
        /// <param name="quality">the quality (0 ~ 100)</param>
        public static void SaveJpeg(this Image img, string outputFileName, int quality)
        {
            // Encoder parameter for image quality
            EncoderParameter qualityParam = new EncoderParameter(Encoder.Quality, quality);

            // Jpeg image codec
            ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");
            if (jpegCodec == null)
                return;
            EncoderParameters encoderParams = new EncoderParameters(1);
            encoderParams.Param[0] = qualityParam;
            img.Save(outputFileName, jpegCodec, encoderParams);
            
        }
    } 
    #endregion
    
}
